package service

import (
	"a-common/auth/loginauth"
	"a-common/config"
	"a-common/constant"
	"a-common/core/redis"
	"a-common/myerr"
	"a-common/utils"
	"a-common/utils/tree"
	"a-system/dao"
	"a-system/domain/entity"
	"a-system/domain/pojo"
	"errors"
	"sync"

	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
)

var (
	sysLoginServiceTemp *SysLoginService
	cryptUtils          = utils.NewCryptUtils()
	captchaUtils        = utils.NewCaptchaUtils()
	emailUtils          = utils.NewEmailUtils()
	loginDao            = dao.NewLoginDao()
	sysLoginServiceOnce sync.Once
	rdCli               = redis.NewRedisCli()

	maxRetryCount = 5
	lockTime      = 10

	// fileHost = "./upload"
)

type SysLoginService struct{}

func NewSysLoginService() *SysLoginService {
	sysLoginServiceOnce.Do(func() {
		sysLoginServiceTemp = &SysLoginService{}
	})
	return sysLoginServiceTemp
}

// Login 用户登陆
func (m *SysLoginService) Login(args *pojo.SysLoginDto, ip string) (int64, string, string, string, string, string, string, error) {
	// 检测账号是否被锁定
	rdKey := constant.PWD_ERR_CNT_KEY + args.Username
	cnt, _ := rdCli.Get(rdKey)
	count := 1
	str, _ := cnt.(string)
	if numInt, err := strconv.Atoi(str); err == nil {
		if numInt > maxRetryCount {
			return 0, "", "", "", "", "", "", myerr.ERR_LOGIN_LOCK
		}
		count = count + numInt
	}
	if err := rdCli.SetEx(rdKey, count, time.Duration(lockTime*60)); err != nil {
		return 0, "", "", "", "", "", "", err
	}

	// 校验验证码
	captchaEnable := config.Config.Login.CaptchaEnable
	if captchaEnable {
		if args.Uuid == "" {
			return 0, "", "", "", "", "", "", myerr.ERR_LOGIN_VERIFY
		}
		ok := captchaUtils.CaptchaVerify(args.Uuid, args.Code)
		if !ok {
			return 0, "", "", "", "", "", "", myerr.ERR_LOGIN_VERIFY
		}
	}

	// 获取数据库数据，并返回给前端
	userPo, err := loginDao.GetUserByAccount(args.Username)
	if err != nil {
		return 0, "", "", "", "", "", "", err
	}
	if userPo.UserId == 0 {
		return 0, "", "", "", "", "", "", myerr.ERR_NOT_ACCOUNT
	}
	// 校验数据库密码
	if !cryptUtils.DeCrypt(userPo.Password, args.Password) {
		return 0, "", "", "", "", "", "", myerr.ERR_PWD
	}
	// 删除锁
	if err := rdCli.Del(rdKey); err != nil {
		return 0, "", "", "", "", "", "", err
	}

	// 构建短token
	token, err := loginauth.CreateShortToken(userPo.UserId, userPo.Username, constant.PC, ip)
	if err != nil {
		return 0, "", "", "", "", "", "", err
	}
	// 构建长token
	refToken, err := loginauth.CreateLongToken(userPo.UserId, userPo.Username, constant.PC, ip)
	if err != nil {
		return 0, "", "", "", "", "", "", err
	}
	// 更新最新登陆ip及时间
	t := time.Now()
	err = sysUserDao.EditByIpAndTime(userPo.UserId, ip, t)
	if err != nil {
		return 0, "", "", "", "", "", "", err
	}
	return userPo.UserId, userPo.Username, userPo.Nickname, userPo.Avatar, t.Format(constant.TIMESTAMP_STR), token, refToken, nil
}

// RefreshToken 刷新token
func (m *SysLoginService) RefreshToken(refToken string) (string, error) {
	return "", nil
}

// GetInfo 获取个人信息
func (m *SysLoginService) GetInfo(uid int64) (vUser entity.SysUserPageVo, treeData []map[string]any, err error) {
	// 获取用户信息
	vUser, err = sysUserDao.Get(uid)
	if err != nil {
		return vUser, nil, err
	}

	// 菜单权限
	if uid == constant.ADMIN_ID {
		// 获取菜单数据
		rows, err := dao.NewSysMenuDao().GetMenuAll()
		if err != nil || len(rows) == 0 {
			return vUser, nil, err
		}

		// 构建菜单树
		treeData = tree.GenerateTree(rows, &tree.TreeConfig{
			Rid:        0,
			IdName:     "id",
			PidName:    "pid",
			WeightName: "weigh",
		})

		return vUser, treeData, err
	}

	// TODO 根据角色获取菜单

	return vUser, treeData, nil
}

func (m *SysLoginService) GetRouters(uid int64) (map[string]string, error) {
	return nil, nil
}

// LoginOut 退出登陆
func (m *SysLoginService) LoginOut(account string) error {
	// 删除会话缓存
	return rdCli.Del(constant.LOGIN_SESSION + account)
}

// CaptchaImage 获取图片验证码
func (m *SysLoginService) CaptchaImage() (id, b64s, answer string, err error) {
	return captchaUtils.Generate()
}

func (m *SysLoginService) CaptchaVerify(c *fiber.Ctx) error {
	id := c.Query("id")
	code := c.Query("code")
	ok := captchaUtils.CaptchaVerify(id, code)
	if !ok {
		return errors.New("验证失败")
	}
	return nil
}

// GetEmailCode 获取邮箱验证码
func (m *SysLoginService) GetEmailCode(email string) error {
	return emailUtils.SendCode(email, "验证码", "获取验证码")
}

// EmailCodeVerify 校验邮箱验证码
func (m *SysLoginService) EmailCodeVerify(email, code string) error {
	ok := emailUtils.Verify(email, code)
	if !ok {
		return errors.New("验证失败")
	}
	return nil
}
